package com.zxy.paycenter.modules.alipay.controller;

import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.zxy.paycenter.common.aop.RequireSignature;
import com.zxy.paycenter.modules.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.util.Map;
import java.util.Properties;

/**
 * @author zxy
 */
@Slf4j
@RestController
@Api(tags = "支付宝回调")
public class AlipayNotifyController extends BaseController {

    @RequireSignature
    @ApiOperation(value = "支付宝支付回调(二维码、H5、网站、商户代扣)")
    @PostMapping(value = "/alipay/notifyUrl")
    public void alipayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // TODO 支付宝回调方法需要修改，支付宝配置信息是从RequireSignature 切面获取,支付宝回调无法返回渠道号等头信息。
        String message = "success";
        log.info("调用支付宝支付回调接口");
        Properties properties = (Properties) request.getAttribute("aliConfig");
        String merchantName = properties.getProperty("merchantName");
        log.info("{}:支付宝支付回调", merchantName);
        Map<String, String> params = getParamsMap(request);
        //验证签名 校验签名
        boolean signVerified = false;
        try {
            signVerified = AlipaySignature.rsaCheckV1(params, properties.getProperty("alipayPublicKey"), properties.getProperty("charset"), properties.getProperty("signType"));
        } catch (AlipayApiException e) {
            log.info("支付宝验签失败", e);
            message = "failed";
        }
        if (signVerified) {
            log.info("支付宝验签成功");
            String notifyType = params.get("notify_type");
            switch (notifyType) {
                case "trade_status_sync":
                    log.info("订单异步通知处理");
                    // 若参数中的appid和填入的appid不相同，则为异常通知
                    if (!properties.getProperty("appId").equals(params.get("app_id"))) {
                        log.info("与付款时的appid不同，此为异常通知");
                        message = "failed";
                    } else {
                        String outTradeNo = params.get("out_trade_no");
                        // TODO 在数据库中查找订单号对应的订单，并将其金额与数据库中的金额对比，若对不上，也为异常通知 (待完善)
                        String status = params.get("trade_status");
                        if ("WAIT_BUYER_PAY".equals(status)) {
                            // 订单状态是正在等待用户付款
                            log.info("{}订单的状态正在等待用户付款", outTradeNo);
                        } else if ("TRADE_CLOSED".equals(status)) {
                            // 订单状态是未付款交易超时关闭，或支付完成后全额退款
                            log.info("{}订单的状态已经关闭", outTradeNo);
                        } else if ("TRADE_SUCCESS".equals(status) || "TRADE_FINISHED".equals(status)) {
                            log.info("订单号:{}付款成功", outTradeNo);
                            // TODO 这里 根据实际业务场景 做相应的操作
                        } else {
                            log.info("未知订单状态:{}", status);
                        }
                    }
                    break;
                case "dut_user_sign":
                    log.info("支付宝用户签约异步通知");
                    String alipayUserId = params.get("alipay_user_id");
                    String externalLogonId = params.get("external_logon_id");
                    String agreementNo = params.get("agreement_no");
                    if (StringUtils.isNotBlank(alipayUserId) && StringUtils.isNotBlank(externalLogonId) && StringUtils.isNotBlank(agreementNo)) {
                        log.info("获取用户签约信息:alipayUserId:{},externalLogonId:{},agreementNo:{}", alipayUserId, externalLogonId, agreementNo);
                        // TODO 这里可以根据实际情况更新用户支付宝签约信息
                    } else {
                        log.info("缺少用户签约的必要信息");
                        message = "failed";
                    }
                    break;
                case "dut_user_unsign":
                    log.info("支付宝用户解约异步通知");
                    String status = params.get("status");
                    if ("UNSIGN".equals(status)) {
                        log.info("用户解约异步通知成功");
                        String userId = params.get("external_logon_id");
                        // TODO 这里可以根据实际情况去除用户支付宝签约信息
                    } else {
                        log.info("用户解约失败status:{}", status);
                        message = "failed";
                    }
                    break;
                default:
                    log.info("其他异步通知{}", notifyType);
                    message = "failed";
                    break;
            }
        } else {
            log.info("支付宝验签失败");
            message = "failed";
        }
        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        out.write(message.getBytes());
        out.flush();
        out.close();
    }

    @RequireSignature
    @ApiOperation(value = "支付宝支付PC端前台回调")
    @GetMapping("/frontRcvResponse")
    public String frontRcvResponse(HttpServletRequest request) {
        try {
            Properties properties = (Properties) request.getAttribute("aliConfig");
            String merchantName = properties.getProperty("merchantName");
            log.info("{}:支付宝支付回调", merchantName);
            //获取支付宝GET过来反馈信息
            Map<String, String> params = getParamsMap(request);
            //商户订单号
            String orderNo = params.get("out_trade_no");
            //前台回调验证签名 v1 or v2
            boolean signVerified = AlipaySignature.rsaCheckV1(params, properties.getProperty("alipayPublicKey"), properties.getProperty("charset"));
            if (signVerified) {
                log.info("订单号{}验证签名结果[成功].", orderNo);
                //处理业务逻辑
            } else {
                log.info("订单号{}验证签名结果[失败].", orderNo);
            }
        } catch (Exception e) {
            log.error("支付宝支付PC端前台回调出错", e);
            //处理异常信息
        }
        //支付成功、跳转到成功页面
        return "success.html";
    }
}
